﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MakeTableTool.Serialization
{
    public class TableSerializer : ITableSerializer_Byte
    {
        ExcelTable excelTable;
        public TableSerializer(ExcelTable excelTable)
        {
            this.excelTable = excelTable;
        }
        public byte[] ConvertToByte()
        {
            using (MemoryStream ms = new MemoryStream())
            {
                var rowDatas = excelTable.rowDatas;
                var tableFields = excelTable.tableFields;
                BinaryWriter bw = new BinaryWriter(ms);
                var rowCnt = rowDatas.Count;
                var columnCnt = tableFields.Count;
                bw.Write(rowCnt);
                bw.Write(columnCnt);

                for (int i = 0; i < columnCnt; i++)
                {
                    var tablieField = tableFields[i];
                    var isBase = true; //暂时废弃的字段,预测用来做表基类

                    bw.Write(tablieField.isBase);
                    bw.Write(tablieField.isList);
                    bw.Write((byte)tablieField.define);
                    bw.Write((byte)tablieField.fieldType);

                }


                for (int i = 0; i < rowCnt; i++)
                {
                    var rowData = rowDatas[i];
                    bw.Write(rowData.Key);//ulong

                    TableRowSerializer rowSerializer = new  TableRowSerializer(rowData);
                    var rowByteData = rowSerializer.ConvertToByte();
                    bw.Write(rowByteData.Length); //int
                    bw.Write(rowByteData);
                }

                return ms.ToArray();
            }
        }
    }

    public class TableRowSerializer : ITableSerializer_Byte
    {
        TableRow rowData;
        public TableRowSerializer(TableRow rowData)
        {
            this.rowData = rowData;
        }
        /// <summary>
        /// 序列化
        /// </summary>
        /// <returns></returns>
        public byte[] ConvertToByte()
        {

            using (MemoryStream ms = new MemoryStream())
            {
                var tableFields = rowData.tableFields;
                var dataValue = rowData.dataValue;
                BinaryWriter bw = new BinaryWriter(ms);
                for (int i = 0; i < dataValue.Count; i++)
                {
                    var tabileField = tableFields[i];
                    var value = dataValue[i];
                    var isList = tabileField.isList; 
                  

                  
                    if (isList)
                    {
                        WriteFieldList(bw, value, tabileField.fieldType);
                    }
                    else
                    {
                        WriteField(bw, value, tabileField.fieldType);
                    }
                }
                return ms.ToArray();
            }
        }

        private void WriteField(BinaryWriter bw,object value, TableField.TableBaseType fieldType)
        {
            var strValue = value.ToString();
            switch (fieldType)
            {
                case TableField.TableBaseType.BYTE:
                    byte byteValue;
                    if (byte.TryParse(strValue, out byteValue))
                    {
                        bw.Write((byte)byteValue);
                    }
                    break;
                case TableField.TableBaseType.SHORT:
                    short shortValue;
                    if (short.TryParse(strValue, out shortValue))
                    {
                        bw.Write((short)shortValue);
                    }
                    break;
                case TableField.TableBaseType.INT:
                    int intValue;
                    if (int.TryParse(strValue, out intValue))
                    {
                        WriteIntValue(bw, (int)intValue);
                    }

                    break;
                case TableField.TableBaseType.FLOAT:
                    float floatValue;
                    if (float.TryParse(strValue, out floatValue))
                    {
                        bw.Write((float)floatValue);
                    }
                    break;
                case TableField.TableBaseType.BOOL:
                    bool boolValue;
                    if (bool.TryParse(strValue, out boolValue))
                    {
                        bw.Write((bool)boolValue);
                    }
                    break;
                case TableField.TableBaseType.STRING:
                    WriteStringValue(bw, strValue);
                    break;
                case TableField.TableBaseType.ULONG:
                    ulong ulongValue;
                    if (ulong.TryParse(strValue, out ulongValue))
                    {
                        WriteLongValue(bw, ulongValue);
                    }
                    break;
            }
        }

        private void WriteFieldList(BinaryWriter bw,object value, TableField.TableBaseType fieldType)
        {
            //列表为空
            if (value == null)
            {
                bw.Write(0);
                return;
            }

            var strValue = value.ToString();
            var strValueList = strValue.Split('~');
            int len = 0;

            if (strValueList.Length > 0)
            {
                len = strValueList.Length;
                
                bw.Write(len); // 写入长度
                for (int i = 0; i < len; i++)
                {
                    var dataStr = strValueList[i];
                    WriteField(bw, dataStr, fieldType);
                }
            }
        }
        #region Write写入数据

        /// <summary>
        /// 二进制存储类型
        /// </summary>
        public enum AttrType : byte
        {
            Type_Null,
            Type_Zero,
            Type_SByte,
            Type_Short,
            Type_Int,
            Type_Long,
            Type_ULong,
            Type_Byte,
            Type_UShort,
            Type_UInt,
        }
        #region WirteInt
        private void WriteIntValue(BinaryWriter bw, int value)
        {
            AttrType attrType = GetIntAttrType(value);
            bw.Write((byte)attrType);
            WriteIntAttr(bw, attrType, value);
        }

        private AttrType GetIntAttrType(int value)
        {
            if (value == 0)
            {
                return AttrType.Type_Zero;
            }
            else if (value >= byte.MinValue && value <= byte.MaxValue)
            {
                return AttrType.Type_Byte;
            }
            else if (value >= sbyte.MinValue && value <= sbyte.MaxValue)
            {
                return AttrType.Type_SByte;
            }
            else if (value >= short.MinValue && value <= short.MaxValue)
            {
                return AttrType.Type_Short;
            }
            else if (value >= ushort.MinValue && value <= ushort.MaxValue)
            {
                return AttrType.Type_UShort;
            }
            else
            {
                return AttrType.Type_Int;
            }
        }

        private void WriteIntAttr(BinaryWriter bw, AttrType attrType, int value)
        {
            switch (attrType)
            {
                case AttrType.Type_Zero:
                    break;
                case AttrType.Type_Byte:
                    bw.Write((byte)value);
                    break;
                case AttrType.Type_SByte:
                    bw.Write((sbyte)value);
                    break;
                case AttrType.Type_Short:
                    bw.Write((short)value);
                    break;
                case AttrType.Type_UShort:
                    bw.Write((ushort)value);
                    break;
                case AttrType.Type_Int:
                    bw.Write((int)value);
                    break;
                default:
                    throw new ArgumentOutOfRangeException(nameof(attrType), attrType, null);
            }
        }

        #endregion



        #region WriteULong
        private void WriteLongValue(BinaryWriter bw, ulong value)
        {
            AttrType attrType = GetUlongAttrType(value);
            bw.Write((byte)attrType);
            WriteUlongAttr(bw, attrType, value);
        }

        private AttrType GetUlongAttrType(ulong value)
        {
            if (value == 0)
            {
                return AttrType.Type_Zero;
            }
            else if (value >= byte.MinValue && value <= byte.MaxValue)
            {
                return AttrType.Type_Byte;
            }
            else if (value >= ushort.MinValue && value <= ushort.MaxValue)
            {
                return AttrType.Type_UShort;
            }
            else if (value >= uint.MinValue && value <= uint.MaxValue)
            {
                return AttrType.Type_UInt;
            }
            else
            {
                return AttrType.Type_ULong;
            }
        }


        private void WriteUlongAttr(BinaryWriter bw, AttrType attrType, ulong value)
        {
            switch (attrType)
            {
                case AttrType.Type_Zero:
                    break;
                case AttrType.Type_Byte:
                    bw.Write((byte)value);
                    break;
                case AttrType.Type_UShort:
                    bw.Write((ushort)value);
                    break;
                case AttrType.Type_UInt:
                    bw.Write((uint)value);
                    break;
                case AttrType.Type_ULong:
                    bw.Write((ulong)value);
                    break;
                default:
                    throw new ArgumentOutOfRangeException(nameof(attrType), attrType, null);
            }
        }
        #endregion


        private void WriteStringValue(BinaryWriter bw, string value)
        {
           
            var strBytes = Encoding.UTF8.GetBytes(value);
            int len = strBytes.Length;
            bw.Write(len);
            if(len > 0)
                bw.Write(strBytes);
        }
        #endregion
    }
}
